﻿#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>

//int AXX(const void* a, const void* b)//比较函数
//{
//	int n1 = *(const int*)a;
//	int n2 = *(const int*)b;
//	return n1 - n2;
//}
//void JH(void* x, void* y, size_t wid)//交换函数
//{
//	char* q = (const void*)x;
//	char* w = (const void*)y;
//	for (int i = 0; i < wid; i++)
//	{
//		char tmp = q[i];
//		q[i] = w[i];
//		w[i] = tmp;
//	}
// }
//void maopao(const void* p1, size_t len, size_t wid, int (*AXX)(const void*, const void*))
//{
//	int* k1 = (const void*)p1;
//	for (int i = 0; i < len - 1; ++i)
//	{
//		int AK = 1;
//		for (int j = 0; j < len - i - 1; ++j)
//		{
//		    char* m1 = (char*)p1 + j * wid;
//		    char* m2 = (char*)p1 + (j + 1) * wid;
//			if ((AXX(m1, m2)) > 0)
//			{
//				JH(m1, m2, wid);
//				AK = 0;
//			}
//		}
//		if (AK)
//		{
//			break;
//		}
//	}
//}
//void print(int arr[], int len)
//{
//	for (int i = 0; i < len; ++i)
//	{
//		printf("%d ", arr[i]);
//	}
//	printf("\n");
//}
//int main()
//{
//	int arr[] = { 5,6,2,4,9,8,1,3,7,10 };
//	int len = sizeof(arr) / sizeof(arr[0]);
//	maopao(arr, len, sizeof(arr[0]), AXX);
//	print(arr, len);
//	return 0;
//}

//数组名是数组首元素地址
//但是有例外
//sizeof(单独一个数组名）-- 不是整个数组大小，单位是字节
//&数组名 -- 数组名表示整个地址，取出的是整个数组的地址
//int main()
//{
//	//int a[] = { 1,2,3,4 };//数组有几个元素？
//
//	//printf("%zd\n", sizeof(a));//16 -- sizeof(数组名)的场景
//	//printf("%zd\n", sizeof(a + 0));//a是首元素的地址-类型是int*, a+0 还是首元素的地址,是地址大小就是4/8
//	//printf("%zd\n", sizeof(*a));//a是首元素的地址,*a就是首元素，大小就是4个字节
//	////*a == a[0] == *(a+0)
//	//printf("%zd\n", sizeof(a + 1));//a是首元素的地址,类型是int*,a+1跳过1个整型，a+1就是第二个元素的地址，4/8
//	//printf("%zd\n", sizeof(a[1]));//a[1]就是第二个元素，大小4个字节
//	//printf("%zd\n", sizeof(&a));//&a是数组的地址，数组的地址也是地址，是地址大小就是4/8个字节
//	//printf("%zd\n", sizeof(*&a));//1. *& 互相抵消了，sizeof(*&a) = sizeof(a) -16
//	////2. &a 是数组的地址，类型是int(*)[4],对数组指针解引用访问的是数组， 计算的是数组的大小 -16
//	////
//	//printf("%zd\n", sizeof(&a + 1));//&a+1是跳过整个数组后的那个位置的地址，是地址就是4/8个字节
//	//printf("%zd\n", sizeof(&a[0])); //首元素的地址，大小4/8个字节
//	//printf("%zd\n", sizeof(&a[0] + 1));//&a[0] + 1 -- 数组第二个元素的地址，大小是4/8个字节
//
//	//数组名是数组首元素地址
//    //但是有例外
//	//sizeof(单独一个数组名）-- 不是整个数组大小，单位是字节
//    //&数组名 -- 数组名表示整个地址，取出的是整个数组的地址
//	//char arr[] = { 'a','b','c','d','e','f' };
//	//printf("%d\n", sizeof(arr));//数组名单独放在sizeof内部了，计算的是数组的大小，单位是字节-6
//	//printf("%d\n", sizeof(arr + 0));//arr是数组名表示首元素的地址，arr+0还是首元素的地址，是地址就是4/8个字节
//	//printf("%d\n", sizeof(*arr));//arr是首元素的地址，*arr就是首元素，大小就是1个字节		//*arr -- arr[0] - *(arr+0)
//	//printf("%d\n", sizeof(arr[1]));//arr[1] 是第二个元素，大小也是1个字
//	//printf("%d\n", sizeof(&arr));//&arr 是数组地址，数组的地址也是地址，大小是4/8个字节  &arr == arr[0]
//	//printf("%d\n", sizeof(&arr + 1));//&arr+1, 跳过整个数组，指向了数组后边的空间，4/8个字节
//	//printf("%d\n", sizeof(&arr[0] + 1));//第二个元素的地址，是地址就是4/8字节
//	
//
//	//char arr[] = { 'a','b','c','d','e','f' };
//	//printf("%d\n", strlen(arr));//arr是首元素的地址，数组中没有\0,就会导致越界访问，结果就是随机的
//	//printf("%d\n", strlen(arr + 0));//arr+0是数组首元素的地址，数组中没有\0,就会导致越界访问，结果就是随机的
//	////printf("%d\n", strlen(*arr));//arr是首元素的地址,*arr是首元素，就是'a','a'的ascii码值是97
//	////就相当于把97作为地址传递给了strlen，strlen得到的就是野指针, 代码是有问题的
//	////printf("%d\n", strlen(arr[1]));//arr[1]--'b'--98,传给strlen函数也是错误的
//	//printf("%d\n", strlen(&arr));//&arr是数组的地址，起始位置是数组的第一个元素的位置，随机值 x
//	//printf("%d\n", strlen(&arr + 1));//随机值 x-6
//	//printf("%d\n", strlen(&arr[0] + 1));//从第2个元素开始向后统计的，得到的也是随机值 x-1
//
//
//	//char arr[] = "abcdef";
//	//printf("%d\n", sizeof(arr));//7
//	//printf("%d\n", sizeof(arr + 0));//8
//	//printf("%d\n", sizeof(*arr));//1
//	//printf("%d\n", sizeof(arr[1]));//1
//	//printf("%d\n", sizeof(&arr));//8
//	//printf("%d\n", sizeof(&arr + 1));//8
//	//printf("%d\n", sizeof(&arr[0] + 1));//8
//
//	//char arr[] = "abcdef";
//	//printf("%d\n", strlen(arr));//6
//	//printf("%d\n", strlen(arr + 0));//6
//	////printf("%d\n", strlen(*arr));//x
//	////printf("%d\n", strlen(arr[1]));//x
//	//printf("%d\n", strlen(&arr));//6
//	//printf("%d\n", strlen(&arr + 1));//x
//	//printf("%d\n", strlen(&arr[0] + 1));//5
//
//	//char* p = "abcdef";
//	//printf("%d\n", sizeof(p));//8
//	//printf("%d\n", sizeof(p + 1));//8
//	//printf("%d\n", sizeof(*p));//1
//	//printf("%d\n", sizeof(p[0]));//1
//	//printf("%d\n", sizeof(&p));//8
//	//printf("%d\n", sizeof(&p + 1));//8
//	//printf("%d\n", sizeof(&p[0] + 1));//8
//
//	//char* p = "abcdef";
//	//printf("%d\n", strlen(p));//6
//	//printf("%d\n", strlen(p + 1));//5
//	////printf("%d\n", strlen(*p));//x
//	////printf("%d\n", strlen(p[0]));//x
//	//printf("%d\n", strlen(&p));//x
//	//printf("%d\n", strlen(&p + 1));//x
//	//printf("%d\n", strlen(&p[0] + 1));//5
//
//	int a[3][4] = { 0 };
//		printf("%d\n", sizeof(a));//a是数组名，单独放在sizeof内部，计算的是数组的大小，单位是字节 - 48 = 3*4*sizeof(int)
//		printf("%d\n", sizeof(a[0][0]));//a[0][0] 是第一行第一个元素，大小4个字节 
//		printf("%d\n", sizeof(a[0]));//a[0]第一行的数组名，数组名单独放在sizeof内部了，计算的是数组的总大小 16 个字节
//		printf("%d\n", sizeof(a[0] + 1));//a[0]第一行的数组名，但是a[0]并没有单独放在sizeof内部，所以这里的数组名a[0]就是
//		//数组首元素的地址，就是&a[0][0],+1后是a[0][1]的地址，大小是4/8个字节
//	
//		printf("%d\n", sizeof(*(a[0] + 1)));//*(a[0] + 1)表示第一行第二个元素，大小就是4
//		printf("%d\n", sizeof(a + 1));//a作为数组名并没有单独放在sizeof内部，a表示数组首元素的地址，是二维数组首元素的地址，也就是
//		//第一行的地址，a+1,跳过一行，指向了第二行，a+1是第二行的地址，a+1是数组指针，是地址大小就是4/8个字节
//	
//		printf("%d\n", sizeof(*(a + 1)));//1.a+1是第二行的地址,*(a+1)就是第二行，计算的是第二行的大小 - 16
//		//2. *(a + 1) == a[1], a[1]是第二行的数组名，sizeof(*(a + 1))就相当于sizeof(a[1]),意思是把第二行的数组名单独放在
//		//sizeof内部，计算的是第二行的大小
//		printf("%d\n", sizeof(&a[0] + 1));//a[0]是第一行的数组名，&a[0]取出的就是数组的地址，就是第一行的地址
//		//&a[0]+1 就是第二行的地址，是地址大小就是4/8个字节
//		printf("%d\n", sizeof(*(&a[0] + 1)));//*(&a[0] + 1)意思是对第二行的地址解引用，访问的就是第二行，大小是16字节
//		printf("%d\n", sizeof(*a));//a作为数组名并没有单独放在sizeof内部，a表示数组首元素的地址，是二维数组首元素的地址，也就是
//		//第一行的地址，*a就是第一行，计算的就是第一行的大小，16字节
//		//*a == *(a+0) == a[0]
//		printf("%d\n", sizeof(a[3]));//a[3]无需真实存在，仅仅通过类型的推断就能算出长度
//		//a[3]是第四行的数组名，单独放在sizeof内部，计算的是第四行的大小，16个字节
//
//	return 0;
//}

//int main()
//{
//	int a[5] = { 5, 4, 3, 2, 1 };
//	int* ptr = (int*)(&a + 1);
//	printf("%d,%d", *(a + 1), *(ptr - 1));
//	return 0;
//}

//int main()
//{
//	int aa[2][5] = { 10,9,8,7,6,5,4,3,2,1 };
//	int* ptr1 = (int*)(&aa + 1);
//	int* ptr2 = (int*)(*(aa + 1));
//	printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));
//	return 0;
//}

//int main()
//{
//	int a[5] = { 1, 2, 3, 4, 5 };
//	int* ptr = (int*)(&a + 1);
//	printf("%d,%d", *(a + 1), *(ptr - 1));
//	return 0;
//}

//在X86环境下
//假设结构体的⼤⼩是20个字节
//程序输出的结果是啥？
//struct Test
//{
//	int Num;
//	char* pcName;
//	short sDate;
//	char cha[2];
//	short sBa[4];
//}*p = (struct Test*)0x100000;
//int main()
//{
//	//p是指针，+1跳过一个结构体（20字节）
//	printf("%p\n", p + 0x1);
//	//强制类型转换为undigned long 型，为整型，+1就是1
//	printf("%p\n", (unsigned long)p + 0x1);
//	//强制类型转换为unsigned int* 型，为int指针，+1跳过4个字节
//	printf("%p\n", (unsigned int*)p + 0x1);
//	return 0;
//}

//int main()
//{
//	int a[3][2] = { (0, 1), (2, 3), (4, 5) };//小括号为逗号表达式，只初始化了1,3,5
//	int* p;
//	p = a[0];
//	printf("%d", p[0]);
//	return 0;
//}

//假设环境是x86环境，程序输出的结果是啥？
//int main()
//{
//	int a[5][5];
//	int(*p)[4];
//	p = a;
//	printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
//	return 0;
//}

//int main()
//{
//	int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//	int* ptr1 = (int*)(&aa + 1);
//	int* ptr2 = (int*)(*(aa + 1));
//	printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));
//	return 0;
//}

//假设环境是x86环境，程序输出的结果是啥？
//int main()
//{
//	int a[5][5];
//	int(*p)[4];//数组指针
//	p = a;
//	printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
//	return 0;
//}

//int main()
//{
//	char* a[] = { "work","at","alibaba" };//指针数组,数组里存放了 3 个指针，每个指针都指向一个字符串
//	char** pa = a;//pa是二级指针，a是数组名
//	pa++;//char指针++,跳过1个字节
//	printf("%s\n", *pa);
//	return 0;
//}

int main()
{
	char* c[] = { "ENTER","NEW","POINT","FIRST" };
	char** cp[] = { c + 3,c + 2,c + 1,c };
	char*** cpp = cp;
	printf("%s\n", **++cpp);
	printf("%s\n", *-- * ++cpp + 3);
	printf("%s\n", *cpp[-2] + 3);
	printf("%s\n", cpp[-1][-1] + 1);
	return 0;
}